#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <cstring>
#include <iostream>

#include <functional>

#include <unordered_map>

#include <mutex>

#include <vector>

#include "err.hpp"
#include "RingQueue.hpp" //收到消息push到队列
#include "lockguard.hpp"
#include "Thread.hpp"

namespace ns_sever
{
    // 一个服务器肯定要有自己的端口号 ./sever 8888
    const static uint16_t default_port = 8888;
    using func_t = std::function<std::string(std::string)>;

    class udp_Sever
    {
    public:
        udp_Sever(uint16_t port = default_port): port_(port)
        {
            pthread_mutex_init(&mtx, nullptr);
            c = new Thread(0, std::bind(&udp_Sever::recv, this));
            p = new Thread(1, std::bind(&udp_Sever::broadcast, this));
        }
        ~udp_Sever()
        {
            pthread_mutex_destroy(&mtx);
            p->join();
            c->join();
            delete p;
            delete c;

        }
        void start()
        {
            // 1.创建sock 本质是打开文件
            sock_ = socket(AF_INET, SOCK_DGRAM, 0);
            if (sock_ < 0)
            {
                std::cerr << "ctreate error" << strerror(errno) << std::endl;
                exit(CREATE_ERR);
            }
            // 2.给服务器指定IP地址和端口号
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local)); // bzero(&local,sizeof(local));
            // 云服务器不需要bind ip地址 服务器会自动指定
            // 自己本地的虚拟机或者物理机器是需要bind ip的
            local.sin_addr.s_addr = INADDR_ANY; // 让sever启动的时候可以bind本主机上任意ip
            local.sin_port = htons(port_);
            local.sin_family = AF_INET;

            // 将套接字字段和文件字段进行绑定

            int n = bind(sock_, (struct sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                std::cerr << "bind error" << strerror(errno) << std::endl;
                exit(BIND_ERR);
            } 
            p->run();
            c->run();
        }
        void recv()
        {
            char buffer[1024];
            // 服务器本质是一个死循环 永远不退出
            while (true)
            {
                // 从socket中获取message

                // 我要知道是谁给我发送的消息
                struct sockaddr_in client;
                socklen_t clientlen = sizeof(client);
                memset(&client, 0, sizeof(client));

                // 收
                ssize_t n = recvfrom(sock_, &buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &clientlen);
                if (n > 0)
                buffer[n] = '\0';
                else continue;

                // 提取client信息 -- debug
                std::string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);
                std::cout << clientip << "-" << clientport << "# " << buffer << std::endl;

                // 构建一个用户并且检查
                
                std::string name = clientip + '-' + to_string(client.sin_port);
                addUser(name, client);

                rq.push(buffer); // 生产

                // // 业务处理
                // std::string responce = severice_(buffer);

                // 此时有一个客户端client给我发消息 并且信息被我捕捉到buffer
                // 需要把client信息读出来 并且把message发回去
                // 2.发
            }
        }
        void addUser(std::string name, const struct sockaddr_in &client)
        {
            LockGuard lockguard(&mtx);
            auto iter = onlineuser.find(name);
            if (iter != onlineuser.end())
                return; // 存在直接返回
            onlineuser.insert({name, client});
        }
        // 用两个线程进行读/写消息

        void broadcast()
        {
            while(true)
            {
            std::string message;
            rq.pop(&message);

            // 这种方式比较好 没有调系统接口
            vector<struct sockaddr_in> v;
            {
                LockGuard lockguard(&mtx);
                for (auto &user : onlineuser)
                {
                    v.push_back(user.second);
                }
            }
            for (auto &a : v)
            {
                sendto(sock_, message.c_str(), message.size(), 0, (struct sockaddr *)&a, sizeof(a));
            }
            }

            // LockGuard lockguard(&mtx); //并不是最优方式
            // for(auto &user:onlineuser)
            // {
            //     std::cout<<"Broadcast message to"<<user.first<<message<<std::endl;
            //     sendto(sock_,message.c_str(),message.size(),0,(struct sockaddr*)&user.second,sizeof(user.second));
            // }
        }

    private:
        int sock_;
        uint16_t port_;
        // func_t severice_; // 我们的网络服务器刚刚解决的是网络IO的问题 现在进行业务处理
        unordered_map<std::string, struct sockaddr_in> onlineuser;
        pthread_mutex_t mtx; // 保护onlineuser
        RingQueue<std::string> rq;
        Thread *c, *p;
    };
} // end ne_sever